//
//  ProcessAutomatismViewController.m
//  CoreAnimation
//
//  Created by 张国文 on 16/10/31.
//  Copyright © 2016年 hangzhouyijiyin. All rights reserved.
//

#import "ProcessAutomatismViewController.h"

@interface ProcessAutomatismViewController ()<CAAnimationDelegate>

@property (nonatomic, strong) UIView *backgroundView;

@property (nonatomic, strong) UIImageView *ballImageV;

@end

@implementation ProcessAutomatismViewController

/**
 *  自动化的含义就是可以运用一些方法将任意动画分割成多个关键帧，然后使用数学函数对帧(分割的最小时间对应的值)进行计算，以真实还原物体动画，因此自动化分为2步：
  1、把任意属性动画分割成多个关键帧。
   2、用一个数学函数表示弹性动画，使得可以对帧(分割的最小时间对应的值，这里是对时间进行处理的，实际上对时间进行处理就是对时间对应的值进行处理)做更加真实的处理(就是最下方的bounceEaseOut) 。这些数学函数可以在（http://www.robertpenner.com/easing）这个网页上面找到。
 */

/**
 *  对于CAKeyframeAnimation来说，如果获得动画之间任意时刻的value值，就可以自定义values的数组了。我们的难点就是获取动画任意时刻的value值。
     任意时刻时刻的value值公式：
 value = (endValue – startValue) × time + startValue;
 那么如果要插入一个类似于CGPoint，CGColorRef或者CATransform3D这种更加复杂类型的值，我们可以简单地对每个独立的元素应用这个方法（也就CGPoint中的x和y值，CGColorRef中的红，蓝，绿，透明值，或者是CATransform3D中独立矩阵的坐标）。我们同样需要一些逻辑在插值之前对对象拆解值，然后在插值之后在重新封装成对象，也就是说需要实时地检查类型。
 
 一旦我们可以用代码获取属性动画的起始值之间的任意插值，我们就可以把动画分割成许多独立的关键帧，然后产出一个线性的关键帧动画。。
 
 注意到我们用了60 x 动画时间（秒做单位）作为关键帧的个数，这时因为Core Animation按照每秒60帧去渲染屏幕更新，所以如果我们每秒生成60个关键帧，就可以保证动画足够的平滑（尽管实际上很可能用更少的帧率就可以达到很好的效果）。
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.backgroundView = [[UIView alloc] initWithFrame:self.view.frame];
    self.backgroundView.backgroundColor = [UIColor colorWithRed:150 / 255.0 green:150 / 255.0 blue:150 / 255.0 alpha:1];
    [self.view addSubview:_backgroundView];
    
    self.ballImageV = [[UIImageView alloc] initWithFrame:CGRectMake(150, 50, 64, 64)]; // 此时center 182,82
    self.ballImageV.image = [UIImage imageWithName:@"ball"];
    [self.backgroundView addSubview:_ballImageV];
    
    UIButton *startButton = [UIButton buttonWithType:UIButtonTypeCustom];
    startButton.frame = CGRectMake(0, 450, 375, 30);
    startButton.backgroundColor = [UIColor whiteColor];
    [startButton setTitle:@"开始小球掉落动画" forState:UIControlStateNormal];
    [startButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [startButton addTarget:self action:@selector(start:) forControlEvents:UIControlEventTouchUpInside];
    startButton.tag = 100;
    [self.backgroundView addSubview:startButton];
}

- (void)start:(UIButton *)button
{
    NSValue *fromValue = [NSValue valueWithCGPoint:CGPointMake(182, 82)];
    NSValue *toValue = [NSValue valueWithCGPoint:CGPointMake(182, 400)];
    CFTimeInterval duration = 2.0;
    // 因为每秒屏幕刷新60次
    NSInteger numFrames = duration * 60;
    NSMutableArray *frames = [NSMutableArray array];
    for (int i = 0; i < numFrames; i++) {
        float time = 2 / (float)numFrames * i;
        time = bounceEaseOut(time);
      //  NSLog(@"time = %f", time);
        [frames addObject:[self interpolateFromValue:fromValue toValue:toValue time:time]];
    }
    NSLog(@"frame = %@", frames);
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.duration = duration;
    animation.delegate = self;
    animation.values = frames;
    [self.ballImageV.layer addAnimation:animation forKey:@"changePosition"];
}

- (void)animationDidStop:(CAKeyframeAnimation *)anim finished:(BOOL)flag
{
    self.ballImageV.layer.position = CGPointMake(182, 400);
}

// 获取对应时间的值
float interpolate(float from, float to, float time)
{
    return (to - from) * time + from;
}

- (id)interpolateFromValue:(id)fromValue toValue:(id)toValue time:(float)time
{
    // 判断类型
    if ([fromValue isKindOfClass:[NSValue class]]) {
        // 获取具体类型
        const char *type = [fromValue objCType];
       // NSLog(@"类型：%s", type);
        // @encode将类型编码成为objCType
        if (strcmp(type, @encode(CGPoint)) == 0) {
            CGPoint from = [fromValue CGPointValue];
            CGPoint to = [toValue CGPointValue];
          
            CGPoint result = CGPointMake(interpolate(from.x, to.x, time), interpolate(from.y, to.y, time));
            if (result.y > 400) {
                
            } else {
                return [NSValue valueWithCGPoint:result];
            }
            
        }
    }
    // 如果不是NSValue类型的，返回一个安全值
    return (time < 0.5)? fromValue :toValue;
    
}

// 这可以起到作用，但效果并不是很好，到目前为止我们所完成的只是一个非常复杂的方式来使用线性缓冲复制CABasicAnimation的行为。这种方式的好处在于我们可以更加精确地控制缓冲，这也意味着我们可以应用一个完全定制的缓冲函数。那么该如何做呢？缓冲背后的数学并不很简单，但是幸运的是我们不需要一一实现它。罗伯特·彭纳有一个网页关于缓冲函数（http://www.robertpenner.com/easing），包含了大多数普遍的缓冲函数的多种编程语言的实现的链接，包括C。

float bounceEaseOut(float t)
{
    if (t < 4/11.0) {
        return (121 * t * t)/16.0;
    } else if (t < 8/11.0) {
        return (363/40.0 * t * t) - (99/10.0 * t) + 17/5.0;
    } else if (t < 9/10.0) {
        return (4356/361.0 * t * t) - (35442/1805.0 * t) + 16061/1805.0;
    }
    return (54/5.0 * t * t) - (513/25.0 * t) + 268/25.0;
}

@end
